---
title: Shadcn UI
description: How to use Slingshot with Shadcn
---

Slingshot has built-in support for Shadcn and can easily be added to your Shadcn project using the CLI.

## Installation

```bash
npx shadcn@latest add "https://saas-js.dev/r/slingshot"
```

## Usage

```tsx
'use client'

import * as FileUpload from '@/components/ui/file-upload'
import { Button } from '@/components/ui/button'
import { Progress } from '@/components/ui/progress'

import { FileUploadRootProps } from '@saas-js/slingshot-react'

export const UploadAvatar = (props: Omit<FileUploadRootProps, 'children'>) => {
  return (
    <FileUpload.Root profile="avatar" maxFiles={1} {...props}>
      <FileUpload.Context>
        {(api) => {
          const slingshotFiles = api.slingshot.getFiles()

          return (
            <>
              <FileUpload.Dropzone>
                <FileUpload.Label>Drag your file(s) here</FileUpload.Label>

                <FileUpload.Trigger asChild>
                  <Button>Choose file(s)</Button>
                </FileUpload.Trigger>
              </FileUpload.Dropzone>

              <FileUpload.ItemGroup>
                {api.acceptedFiles.map((file) => {
                  const slingshotFile = api.slingshot
                    .getFiles()
                    ?.find((f) => f.name === file.name)

                  return (
                    <FileUpload.Item
                      key={file.name}
                      file={file}
                      className="relative items-center"
                    >
                      <FileUpload.ItemPreview type="image/*">
                        <FileUpload.ItemPreviewImage />
                      </FileUpload.ItemPreview>
                      <div className="flex flex-col flex-1">
                        <FileUpload.ItemName />
                        <FileUpload.ItemSizeText />
                      </div>

                      <div className="text-xs text-gray-500">
                        {slingshotFile?.progress &&
                          (slingshotFile?.status === 'done' ? (
                            <span>Done</span>
                          ) : (
                            <span>{slingshotFile?.progress}%</span>
                          ))}
                      </div>

                      {slingshotFile?.progress &&
                        slingshotFile?.status !== 'done' && (
                          <Progress
                            value={slingshotFile?.progress ?? 0}
                            className="absolute bottom-0 right-0 left-0 h-1"
                          />
                        )}

                      <FileUpload.ItemDeleteTrigger asChild>
                        <Button variant="ghost" size="icon">
                          ✕
                        </Button>
                      </FileUpload.ItemDeleteTrigger>
                    </FileUpload.Item>
                  )
                })}
              </FileUpload.ItemGroup>

              <FileUpload.HiddenInput />

              {slingshotFiles.map(({ url }) => (
                <input
                  key={url}
                  type="hidden"
                  name={props.profile}
                  value={url?.split('?')[0]}
                />
              ))}
            </>
          )
        }}
      </FileUpload.Context>
    </FileUpload.Root>
  )
}
```
